package com.fast.fastenjoy.base.dao.impl;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang.StringUtils;
import org.hibernate.Query;
import org.hibernate.SQLQuery;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.transform.ResultTransformer;
import org.springframework.beans.factory.annotation.Qualifier;

import com.fast.fastenjoy.base.dao.BaseDao;
import com.fast.fastenjoy.base.dao.sqltemplate.SQLTemplate;
import com.fast.fastenjoy.base.dao.sqltemplate.domain.SqlMap;
import com.fast.fastenjoy.base.dao.sqltemplate.domain.SqlMapType;
import com.fast.fastenjoy.base.dao.sqltemplate.domain.SqlOpType;
import com.fast.fastenjoy.base.dao.sqltemplate.transformer.ResultTransformerEx;
import com.fast.fastenjoy.base.domain.Page;

/**
 * 公共处理数据层
 * @author chenliaohua
 *
 * @param <T>
 */
@SuppressWarnings("unchecked")
public abstract class GenericDaoImpl<T extends Serializable> implements BaseDao<T>{

	private static final String HTL_GET_ALL = "from %s";
	
	private static final String SQL_GET_ALL = " where ";
	
	static{
		SQLTemplate.init();
	}
	
	@Qualifier("sessionFactory")
	private SessionFactory sessionFactory;
	
	public Session getSession(){
		return sessionFactory.openSession();
	}
	
	public Serializable save(T entity) {
		
		Serializable id = null;
		Session session = null;
		
		try{
			session = getSession();
			id = session.save(entity);
		}catch(Throwable e){
			e.printStackTrace();
		}finally{
			if(session != null){
				flushAndClear(session);
			}
		}
		
		return id;
	}

	public void batchSave(List<T> entityes) {
		
		if(entityes == null || entityes.isEmpty()){
			return;
		}
		
		Session session = null;
		
		try{
			session = getSession();
			Iterator<T> iterator = entityes.iterator();
		    while (iterator.hasNext()) {
		    	session.save(iterator.next());
		    }
		}catch(Throwable e){
			e.printStackTrace();
		}finally{
			if(session != null){
				flushAndClear(session);
			}
		}
	}

	public void update(T entity) {
		
		Session session = null;
		
		try{
			session = getSession();
			session.update(entity);
		}catch(Throwable e){
			e.printStackTrace();
		}finally{
			if(session != null){
				flushAndClear(session);
			}
		}
	}

	public void batchUpdate(List<T> entityes) {
		
		if(entityes == null || entityes.isEmpty()){
			return;
		}
		
		Session session = null;
		
		try{
			session = getSession();
			Iterator<T> iterator = entityes.iterator();
		    while (iterator.hasNext()) {
		    	session.update(iterator.next());
		    }
		}catch(Throwable e){
			e.printStackTrace();
		}finally{
			if(session != null){
				flushAndClear(session);
			}
		}
	}

	public void saveOrUpdate(T entity) {
		
		Session session = null;
		
		try{
			session = getSession();
			session.saveOrUpdate(entity);
		}catch(Throwable e){
			e.printStackTrace();
		}finally{
			if(session != null){
				flushAndClear(session);
			}
		}
	}

	public void delete(T entity) {
		
		Session session = null;
		
		try{
			session = getSession();
			session.delete(entity);
		}catch(Throwable e){
			e.printStackTrace();
		}finally{
			if(session != null){
				flushAndClear(session);
			}
		}
	}

	public void batchDelete(List<T> entityes) {
		
		if(entityes == null || entityes.isEmpty()){
			return;
		}
		
		Session session = null;
		
		try{
			session = getSession();
			for(T t : entityes){
				session.delete(t);
			}
		}catch(Throwable e){
			e.printStackTrace();
		}finally{
			if(session != null){
				flushAndClear(session);
			}
		}
	}

	public T get(Class<T> entityClass, Serializable id) {
		
		Session session = null;
		T obj = null;
		
		try{
			session = getSession();
			obj = (T)session.get(entityClass, id);
		}catch(Throwable e){
			e.printStackTrace();
		}finally{
			if(session != null){
				flushAndClear(session);
			}
		}
		
		return obj;
	}

	public List<T> getAll(Class<T> entityClass) {
		
		return getAll(entityClass, null);
	}
	
	public List<T> getAll(Class<T> entityClass, String whereHql) {
		
		if(entityClass == null){
			return null;
		}
		
		String entityName = entityClass.getSimpleName();
		
		StringBuffer sb = new StringBuffer(String.format(HTL_GET_ALL, entityName));
		if(StringUtils.isNotEmpty(whereHql)){
			sb.append(SQL_GET_ALL).append(whereHql);
		}
		
		return (List<T>) findByHql(sb.toString(), null);
	}
	
	public int executeByHql(String hql, Map<String, Object> paramMap){
		
		Session session = null;
		int count = 0;
		
		try{
			session = getSession();
			Query query = session.createQuery(hql);
			_builderQueryParam(query, paramMap);
			count = query.executeUpdate();
		}catch(Throwable e){
			e.printStackTrace();
		}finally{
			if(session != null){
				flushAndClear(session);
			}
		}
		
		return count;
	}
	
	public Object findObjectByHql(String hql, Map<String, Object> paramMap){
		
		List<?> list = findByHql(hql, paramMap);
		
		return list != null && list.isEmpty() ? list.get(0) : null;
	}
	
	public List<?> findByHql(String hql, Map<String, Object> paramMap){
		
		Session session = null;
		List<T> list = new ArrayList<T>();
		
		try{
			session = getSession();
			Query query = session.createQuery(hql);
			_builderQueryParam(query, paramMap);
			list = query.list();
		}catch(Throwable e){
			e.printStackTrace();
		}finally{
			if(session != null){
				flushAndClear(session);
			}
		}
		
		return list;
	}
	
	public Page findPageByHql(String hql, Map<String, Object> paramMap, Page page){
		
		Session session = null;
		Page builderPage = null;
		
		try{
			session = getSession();
			
			Query query = session.createQuery(hql);
			
			_builderQueryParam(query, paramMap);
			builderPage = _builderPage(page, query);
			builderPage.setResult(query.list());
			
		}catch(Throwable e){
			e.printStackTrace();
		}finally{
			if(session != null){
				flushAndClear(session);
			}
		}
		
		return builderPage;
	}

	public int executeBySql(String sql, Map<String, Object> paramMap){
		
		int executeUpdate = 0;
		Session session = null;
		
		try{
			session = getSession();
			SQLQuery query = session.createSQLQuery(sql);
			_builderQueryParam(query, paramMap);
			executeUpdate = query.executeUpdate();
		}catch(Throwable e){
			e.printStackTrace();
		}finally{
			if(session != null){
				flushAndClear(session);
			}
		}
		
		return executeUpdate;
	}
	
	public Object findObjectBySql(String sql, Map<String, Object> paramMap, Class<?> entity){
		List<?> list = findBySql(sql, paramMap, entity);
		return list != null && !list.isEmpty() ? list.get(0) : null;
	}
	
	public Object findObjectBySql(String sql, Map<String, Object> paramMap, ResultTransformer resultTransformer){
		List<?> list = findBySql(sql, paramMap, resultTransformer);
		return list != null && !list.isEmpty() ? list.get(0) : null;
	}
	
	public List<?> findBySql(String sql, Map<String, Object> paramMap, Class<?> entity){
		
		return _findBySql(sql, paramMap, entity, null);
	}
	
	public List<?> findBySql(String sql, Map<String, Object> paramMap, ResultTransformer resultTransformer){
		
		return _findBySql(sql, paramMap, null, resultTransformer);
	}
	
	public Page findPageBySql(String sql, Map<String, Object> paramMap, Class<?> entityClass, Page page){
		
		return _findPageBySql(sql, paramMap, entityClass, null, page);
	}
	
	public Page findPageBySql(String sql, Map<String, Object> paramMap, ResultTransformer resultTransformer, Page page){
		
		return _findPageBySql(sql, paramMap, null, resultTransformer, page);
	}
	
	public int executeBySqlId(String sqlId, Map<String, Object> paramMap){
		
		SqlMap sqlMap = SQLTemplate.getSqlMap(sqlId, SqlOpType.EXECUTE);
		
		String sql = SQLTemplate.builderSqL(sqlMap, paramMap);
		
		if(SqlMapType.HQL == sqlMap.getSqlType()){
			return executeBySql(sql, paramMap);
		}else{
			return executeBySql(sql, paramMap);
		}
	}
	
	public Object findObjectBySqlId(String sqlId, Map<String, Object> paramMap){
		List<?> list = findBySqlId(sqlId, paramMap);
		return list != null && !list.isEmpty() ? list.get(0) : null;
	}
	
	public List<?> findBySqlId(String sqlId, Map<String, Object> paramMap){
		
		List<?> list = new ArrayList<Object>();
		
		SqlMap sqlMap = SQLTemplate.getSqlMap(sqlId, SqlOpType.QUERY);
		String sql = SQLTemplate.builderSqL(sqlMap, paramMap);
		
		if(SqlMapType.HQL == sqlMap.getSqlType()){
			list = findByHql(sql, paramMap);
		}else{
			list = findBySql(sql, paramMap, sqlMap.getResultTransformer());
		}
		
		return list;
	}
	
	public Page findPageBySqlId(String sqlId, Map<String, Object> paramMap, Page page){
		
		SqlMap sqlMap = SQLTemplate.getSqlMap(sqlId, SqlOpType.QUERY);
		String sql = SQLTemplate.builderSqL(sqlMap, paramMap);
		
		if(SqlMapType.HQL == sqlMap.getSqlType()){
			return findPageByHql(sql, paramMap, page);
		}else{
			return findPageBySql(sql, paramMap, sqlMap.getResultTransformer(), page);
		}
	}

	private List<?> _findBySql(String sql, Map<String, Object> paramMap, Class<?> entityClass, ResultTransformer resultTransformer){
		
		List<T> list = new ArrayList<T>();
		Session session = null;
		
		try{
			session = getSession();
			
			SQLQuery query = session.createSQLQuery(sql);
			
			_builderQueryParam(query, paramMap);
			
			if(entityClass != null){
				query.addEntity(entityClass);
			}else if(resultTransformer != null){
				if(resultTransformer instanceof ResultTransformerEx){
					ResultTransformerEx rtf = (ResultTransformerEx)resultTransformer;
					if(rtf.isHibernateMapping()){
						query.addEntity(rtf.getEntityClass());
					}else{
						query.setResultTransformer(resultTransformer);
					}
				}else{
					query.setResultTransformer(resultTransformer);
				}
			}
			
			list = query.list();
			
		}catch(Throwable e){
			e.printStackTrace();
		}finally{
			if(session != null){
				flushAndClear(session);
			}
		}
		
		return list;
	}
	
	private Page _findPageBySql(String sql, Map<String, Object> paramMap, Class<?> entityClass, 
			ResultTransformer resultTransformer,
			Page page){
		
		Page builderPage = null;
		Session session = null;
		
		try{
			session = getSession();
			
			SQLQuery query = session.createSQLQuery(sql);
			
			_builderQueryParam(query, paramMap);
			builderPage = _builderPage(page, query);
			
			if(entityClass != null){
				query.addEntity(entityClass);
			}else if(resultTransformer != null){
				if(resultTransformer instanceof ResultTransformerEx){
					ResultTransformerEx rtf = (ResultTransformerEx)resultTransformer;
					if(rtf.isHibernateMapping()){
						query.addEntity(rtf.getEntityClass());
					}else{
						query.setResultTransformer(resultTransformer);
					}
				}else{
					query.setResultTransformer(resultTransformer);
				}
			}
			
			builderPage.setResult(query.list());
			
		}catch(Throwable e){
			e.printStackTrace();
		}finally{
			if(session != null){
				flushAndClear(session);
			}
		}
		
		return builderPage;
	}
	
	private Page _builderPage(Page page, Query query){
		
		Page tempPage = page;
		
		if(tempPage == null){
			tempPage = Page.getInstance();
		}
		
		query.setFirstResult(Page.getStartOfPage(tempPage.getStartIndex(), tempPage.getPageSize()));
		query.setMaxResults(tempPage.getPageSize());
		
		return tempPage;
	}
	
	private void _builderQueryParam(Query query, Map<String, Object> paramMap){
		if(query != null && paramMap != null && !paramMap.isEmpty()){
			Iterator<String> iterator = paramMap.keySet().iterator();
			while(iterator.hasNext()){
				String key = iterator.next();
				query.setParameter(key, paramMap.get(key));
			}
		}
	}
	
	public BaseDao<T> flushAndClear(Session session){
		return this.flush(session).clear(session);
	}
	
	public BaseDao<T> flush(Session session) {
		
		session.flush();
		
		return this;
	}

	public BaseDao<T> clear(Session session) {
		
		session.clear();
		
		return this;
	}
	
	public SessionFactory getSessionFactory() {
		return sessionFactory;
	}

	public void setSessionFactory(SessionFactory sessionFactory) {
		this.sessionFactory = sessionFactory;
	}

}
